﻿@using Microsoft.JSInterop;
@inject IJSRuntime _js

<div class="wavesurfer-ui-container">
    <div class="wavesurfer-ui-main" id="sel_@_mainDivGuid"></div>
    @if(ShowTimeline) {
        <div class="wavesurfer-ui-timeline" id="sel_@_timelineDivGuid"></div>
    }
    @if (ShowMinimap)
    {
        <div class="wavesurfer-ui-minimap" id="sel_@_minimapDivGuid"></div>
    }
</div>
@if (ShowDefaultToolbar)
{
    <div class="wavesurfer-ui-toolbar">
        <div class="wavesurfer-ui-toolbar-left">
            <button @onclick="PlayPause" aria-label="Play/Pause">@TextPlayPause</button>
        </div>
        <div class="wavesurfer-ui-toolbar-right">
            <div class="wavesurfer-ui-toolbar-right-container">
                <span>@TextZoom</span>
                <div class="btn-group btn-group-sm" role="group" aria-label="Zoom values">
                    <button type="button" class="@(_actualZoomLevel == 0 ? "active":"")" @onclick="() => Zoom(0)">@TextDefault</button>
                    <button type="button" class="@(_actualZoomLevel == 2 ? "active":"")" @onclick="() => Zoom(2)">2x</button>
                    <button type="button" class="@(_actualZoomLevel == 4 ? "active":"")" @onclick="() => Zoom(4)">4x</button>
                    <button type="button" class="@(_actualZoomLevel == 8 ? "active":"")" @onclick="() => Zoom(8)">8x</button>
                    <button type="button" class="@(_actualZoomLevel == 16 ? "active":"")" @onclick="() => Zoom(16)">16x</button>
                    <button type="button" class="@(_actualZoomLevel == 32 ? "active":"")" @onclick="() => Zoom(32)">32x</button>
                </div>
            </div>
        </div>
    </div>
}

@code {
    private WavesurferJsInterop? _interop;
    private int _actualZoomLevel = 0;

    private Guid _mainDivGuid;
    private Guid _timelineDivGuid;
    private Guid _minimapDivGuid;

    //Component parameters
    [Parameter]
    public EventCallback<float> OnAudioProcess { get; set; }
    [Parameter]
    public EventCallback OnDblClick { get; set; }
    [Parameter]
    public EventCallback OnDestroy { get; set; }
    [Parameter]
    public EventCallback<string> OnError { get; set; }
    [Parameter]
    public EventCallback OnFinish { get; set; }
    [Parameter]
    public EventCallback OnInteraction { get; set; }
    [Parameter]
    public EventCallback<int> OnLoading { get; set; }
    [Parameter]
    public EventCallback<bool> OnMute { get; set; }
    [Parameter]
    public EventCallback OnPause { get; set; }
    [Parameter]
    public EventCallback OnPlay { get; set; }
    [Parameter]
    public EventCallback OnReady { get; set; }
    [Parameter]
    public EventCallback<object> OnScroll { get; set; }
    [Parameter]
    public EventCallback<float> OnSeek { get; set; }
    [Parameter]
    public EventCallback<float> OnVolume { get; set; }
    [Parameter]
    public EventCallback<int> OnZoom { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionCreated { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionUpdated { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionUpdateEnd { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionRemoved { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionPlay { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionIn { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionOut { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionMouseEnter { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionMouseLeave { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionClick { get; set; }
    [Parameter]
    public EventCallback<WavesurferRegion> OnRegionDblClick { get; set; }
    [Parameter]
    public EventCallback<WavesurferMarker> OnMarkerClick { get; set; }
    [Parameter]
    public string? Url { get; set; }
    [Parameter]
    public bool ShowDefaultToolbar { get; set; } = false;
    [Parameter]
    public bool ShowTimeline { get; set; } = true;
    [Parameter]
    public bool ShowMinimap { get; set; } = true;
    [Parameter]
    public bool ShowMarkers { get; set; } = true;
    [Parameter]
    public string TextPlayPause { get; set; } = "Play/Pause";
    [Parameter]
    public string TextZoom { get; set; } = "Zoom";
    [Parameter]
    public string TextDefault { get; set; } = "Default";
    [Parameter]
    public float AudioProcessEventThreshold { get; set; } = 0.1f;
    [Parameter]
    public bool RegionDragSelection { get; set; } = false;
    [Parameter]
    public IEnumerable<WavesurferOption>? Options { get; set; }

    private readonly List<WavesurferOption> _defaultOptions = new()
    {
        new WavesurferOption(WavesurferOptionKey.normalize, true),
        new WavesurferOption(WavesurferOptionKey.progressColor, "#00629b"),
        new WavesurferOption(WavesurferOptionKey.waveColor, "#428bca")
    };

    //Internal component methods
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            //starting upon rendering target divs
            if (_interop != null)
            {
                await _interop.Create(_mainDivGuid, _timelineDivGuid, _minimapDivGuid, ShowTimeline, ShowMinimap, ShowMarkers, AudioProcessEventThreshold, RegionDragSelection, _defaultOptions, Options);

                //preload file if URL already supplied
                if (Url != null)
                {
                    await Load(Url);
                }
            }
        }
    }

    protected override void OnInitialized()
    {
        _mainDivGuid = Guid.NewGuid();
        _timelineDivGuid = Guid.NewGuid();
        _minimapDivGuid = Guid.NewGuid();
        _interop = new WavesurferJsInterop(_js, DotNetObjectReference.Create<WavesurferPlayer>(this));
    }

    public async ValueTask DisposeAsync()
    {
        if (_interop != null)
        {
            var module = await _interop.GetModuleTask();
            await module.InvokeVoidAsync("destroy");
        }
    }

    //Utility methods
    private async Task CallVoidMethodInterop(string method, params object?[]? args)
    {
        if (_interop != null)
        {
            var module = await _interop.GetModuleTask();
            await module.InvokeVoidAsync(method, args);
        }
    }

    private async Task<T?> CallMethodInterop<T>(string method, params object?[]? args)
    {
        var result = default(T);

        if (_interop != null)
        {
            var module = await _interop.GetModuleTask();
            result = await module.InvokeAsync<T>(method, args);
        }

        return result;
    }

    //External methods
    public async Task CancelAjax()
    {
        await CallVoidMethodInterop("cancelAjax");
    }

    public async Task Empty()
    {
        await CallVoidMethodInterop("empty");
    }

    public async Task<object?> GetActivePlugins()
    {
        return await CallMethodInterop<object>("getActivePlugins");
    }

    public async Task<string?> GetBackgroundColor()
    {
        return await CallMethodInterop<string>("getBackgroundColor");
    }

    public async Task<float?> GetCurrentTime()
    {
        return await CallMethodInterop<float>("getCurrentTime");
    }

    public async Task<string?> GetCursorColor()
    {
        return await CallMethodInterop<string>("getCursorColor");
    }

    public async Task<float?> GetDuration()
    {
        return await CallMethodInterop<float>("getDuration");
    }

    public async Task<float?> GetPlaybackRate()
    {
        return await CallMethodInterop<float>("getPlaybackRate");
    }

    public async Task<string?> GetProgressColor()
    {
        return await CallMethodInterop<string>("getProgressColor");
    }

    public async Task<float?> GetVolume()
    {
        return await CallMethodInterop<float>("getVolume");
    }

    public async Task<bool?> GetMute()
    {
        return await CallMethodInterop<bool>("getMute");
    }

    public async Task<object?> GetFilters()
    {
        return await CallMethodInterop<object>("getFilters");
    }

    public async Task<string?> GetWaveColor()
    {
        return await CallMethodInterop<string>("getWaveColor");
    }

    public async Task<object?> ExportPCM(int? length = null, int? accuracy = null, bool? noWindow = null, int? start = null)
    {
        return await CallMethodInterop<object>("exportPCM", length, accuracy, noWindow, start);
    }

    public async Task<string?> ExportImage(string format, int? quality = null, string? type = null)
    {
        return await CallMethodInterop<string>("exportImage", format, quality, type);
    }

    public async Task<bool?> IsPlaying()
    {
        return await CallMethodInterop<bool>("isPlaying");
    }

    public async Task Load(string url, object? peaks = null, string? preload = null)
    {
        await CallVoidMethodInterop("load", url, peaks, preload);
    }

    public async Task LoadBlob(string url)
    {
        await CallVoidMethodInterop("loadBlob", url);
    }

    public async Task Pause()
    {
        await CallVoidMethodInterop("pause");
    }

    public async Task Play(float? start = null, float? end = null)
    {
        await CallVoidMethodInterop("play", start, end);
    }

    public async Task PlayPause()
    {
        await CallVoidMethodInterop("playPause");
    }

    public async Task SeekAndCenter(float progress)
    {
        await CallVoidMethodInterop("seekAndCenter", progress);
    }

    public async Task SeekTo(float progress)
    {
        await CallVoidMethodInterop("seekTo", progress);
    }

    public async Task SetBackgroundColor(string color)
    {
        await CallVoidMethodInterop("setBackgroundColor", color);
    }

    public async Task SetCursorColor(string color)
    {
        await CallVoidMethodInterop("setCursorColor", color);
    }

    public async Task SetHeight(int height)
    {
        await CallVoidMethodInterop("setHeight", height);
    }

    public async Task SetFilter(object filters)
    {
        await CallVoidMethodInterop("setFilter", filters);
    }

    public async Task SetPlaybackRate(float rate)
    {
        await CallVoidMethodInterop("setPlaybackRate", rate);
    }

    public async Task SetPlayEnd(float position)
    {
        await CallVoidMethodInterop("setPlayEnd", position);
    }

    public async Task SetVolume(float newVolume)
    {
        await CallVoidMethodInterop("setVolume", newVolume);
    }

    public async Task SetMute(bool mute)
    {
        await CallVoidMethodInterop("setMute", mute);
    }

    public async Task SetProgressColor(string color)
    {
        await CallVoidMethodInterop("setProgressColor", color);
    }

    public async Task SetWaveColor(string color)
    {
        await CallVoidMethodInterop("setWaveColor", color);
    }

    public async Task Skip(float offset)
    {
        await CallVoidMethodInterop("skip", offset);
    }

    public async Task SkipBackward()
    {
        await CallVoidMethodInterop("skipBackward");
    }

    public async Task SkipForward()
    {
        await CallVoidMethodInterop("skipForward");
    }

    public async Task SetSinkId(string deviceId)
    {
        await CallVoidMethodInterop("setSinkId", deviceId);
    }

    public async Task Stop()
    {
        await CallVoidMethodInterop("stop");
    }

    public async Task ToggleMute()
    {
        await CallVoidMethodInterop("toggleMute");
    }

    public async Task ToggleInteraction()
    {
        await CallVoidMethodInterop("toggleInteraction");
    }

    public async Task ToggleScroll()
    {
        await CallVoidMethodInterop("toggleScroll");
    }

    public async Task Zoom(int pxPerSec)
    {
        _actualZoomLevel = pxPerSec;
        await CallVoidMethodInterop("zoom", pxPerSec);
    }

    public async Task<WavesurferRegion?> RegionAddRegion(IEnumerable<WavesurferRegionOption>? regionOptions)
    {
        return await CallMethodInterop<WavesurferRegion>("regionAddRegion", WavesurferRegionOptionsService.GetObjectFromRecords(regionOptions));
    }

    public async Task<WavesurferRegion?> RegionAddRegion(WavesurferRegion regionData)
    {
        return await CallMethodInterop<WavesurferRegion>("regionAddRegion", regionData);
    }

    public async Task RegionClearRegions()
    {
        await CallVoidMethodInterop("regionClearRegions");
    }

    public async Task RegionEnableDragSelection(IEnumerable<WavesurferRegionOption>? regionOptions)
    {
        await CallVoidMethodInterop("regionEnableDragSelection", WavesurferRegionOptionsService.GetObjectFromRecords(regionOptions));
    }

    public async Task<IEnumerable<WavesurferRegion>?> RegionList()
    {
        return await CallMethodInterop<IEnumerable<WavesurferRegion>?>("regionList");
    }
    
    public async Task RegionListUpdate(IEnumerable<WavesurferRegion> regionList)
    {
        await CallVoidMethodInterop("regionListUpdate", regionList);
    }
    
    public async Task<WavesurferMarker?> MarkerAddMarker(WavesurferMarker markerData)
    {
        return await CallMethodInterop<WavesurferMarker>("markerAddMarker", markerData);
    }

    public async Task MarkerClearMarkers()
    {
        await CallVoidMethodInterop("markerClearMarkers");
    }

    //JS Events
    [JSInvokable]
    public async Task OnWavesurferAudioProcess(float position)
    {
        await OnAudioProcess.InvokeAsync(position);
    }

    [JSInvokable]
    public async Task OnWavesurferDblClick()
    {
        await OnDblClick.InvokeAsync();
    }
    
    [JSInvokable]
    public async Task OnWavesurferDestroy()
    {
        await OnDestroy.InvokeAsync();
    }
    
    [JSInvokable]
    public async Task OnWavesurferError(string errorMessage)
    {
        await OnError.InvokeAsync(errorMessage);
    }

    [JSInvokable]
    public async Task OnWavesurferFinish()
    {
        await OnFinish.InvokeAsync();
    }
    
    [JSInvokable]
    public async Task OnWavesurferInteraction()
    {
        await OnInteraction.InvokeAsync();
    }
    
    [JSInvokable]
    public async Task OnWavesurferLoading(int percent)
    {
        await OnLoading.InvokeAsync(percent);
    }
    
    [JSInvokable]
    public async Task OnWavesurferMute(bool status)
    {
        await OnMute.InvokeAsync(status);
    }
    
    [JSInvokable]
    public async Task OnWavesurferPause()
    {
        await OnPause.InvokeAsync();
    }
    
    [JSInvokable]
    public async Task OnWavesurferPlay()
    {
        await OnPlay.InvokeAsync();
    }
    
    [JSInvokable]
    public async Task OnWavesurferReady()
    {
        await OnReady.InvokeAsync();
    }
    
    [JSInvokable]
    public async Task OnWavesurferScroll(object scrollEvent)
    {
        await OnScroll.InvokeAsync(scrollEvent);
    }

    [JSInvokable]
    public async Task OnWavesurferSeek(float seekCoef)
    {
        await OnSeek.InvokeAsync(seekCoef);
    }

    [JSInvokable]
    public async Task OnWavesurferVolume(float volume)
    {
        await OnVolume.InvokeAsync(volume);
    }
    
    [JSInvokable]
    public async Task OnWavesurferZoom(int minPxPerSec)
    {
        await OnZoom.InvokeAsync(minPxPerSec);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionCreated(WavesurferRegion region)
    {
        await OnRegionCreated.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionUpdated(WavesurferRegion region)
    {
        await OnRegionUpdated.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionUpdateEnd(WavesurferRegion region)
    {
        await OnRegionUpdateEnd.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionRemoved(WavesurferRegion region)
    {
        await OnRegionRemoved.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionPlay(WavesurferRegion region)
    {
        await OnRegionPlay.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionIn(WavesurferRegion region)
    {
        await OnRegionIn.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionOut(WavesurferRegion region)
    {
        await OnRegionOut.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionMouseEnter(WavesurferRegion region)
    {
        await OnRegionMouseEnter.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionMouseLeave(WavesurferRegion region)
    {
        await OnRegionMouseLeave.InvokeAsync(region);
    }

    [JSInvokable]
    public async Task OnWavesurferRegionClick(WavesurferRegion region)
    {
        await OnRegionClick.InvokeAsync(region);
    }
    
    [JSInvokable]
    public async Task OnWavesurferRegionDblClick(WavesurferRegion region)
    {
        await OnRegionDblClick.InvokeAsync(region);
    }
    
    [JSInvokable]
    public async Task OnWavesurferMarkerClick(WavesurferMarker marker)
    {
        await OnMarkerClick.InvokeAsync(marker);
    }

}


